import { collectNodes, isLeafNode } from 'ali-react-table';
import cx from 'classnames';
import React from 'react';
import { arrayHelpers, Form, FormItem } from 'react-antd-xform';
import { FormArrayLayoutInput } from 'react-antd-xform/dist/form/type';
import { arrayTable } from '.';

const range = (n: number) => {
  const result: number[] = [];
  for (let i = 0; i < n; i++) {
    result.push(i);
  }
  return result;
};

/** 从 JSX 节点中抽取 <FormItem /> 与 <Form.ItemView /> 以及 arrayTable.Group */
function extractFormItemJSXElements(
  node: any,
  startColIndex = 0,
): { result: any[]; flatCount: number } {
  if (node == null) {
    return { result: [], flatCount: 0 };
  }

  if (node.type == React.Fragment) {
    return extractFormItemJSXElements(node.props.children, startColIndex);
  }

  let flatCount = 0;
  const result: any[] = [];
  const array: any[] = React.Children.toArray(node);

  for (const item of array) {
    if (item?.type === FormItem || item?.type === Form.ItemView) {
      result.push({ ...item, colIndex: startColIndex + flatCount });
      flatCount += 1;
    } else if (item?.type === arrayTable.Group) {
      const subRet = extractFormItemJSXElements(
        item.props.children,
        startColIndex + flatCount,
      );
      result.push({
        ...item,
        children: subRet.result,
      });
      flatCount += subRet.flatCount;
    }
  }
  return { result, flatCount };
}

/** 从 JSX 节点中抽取 <Form.Effect /> */
function extractFormEffectJSXElements(node: any): any[] {
  const isFormEffectJSXElement = (item: any) =>
    item?.type === Form.Effect || item?.props?.['x-effect'];

  if (node == null) {
    return [];
  }
  if (node.type == React.Fragment) {
    return React.Children.toArray(node.props.children).filter(
      isFormEffectJSXElement,
    );
  } else {
    return React.Children.toArray(node).filter(isFormEffectJSXElement);
  }
}

/** 从 JSX 节点中抽取 <Form.Check /> */
function extractFormCheckJSXElements(node: any): any[] {
  const isFormCheckJSXElement = (item: any) => item?.type === Form.Check;

  if (node == null) {
    return [];
  }
  if (node.type == React.Fragment) {
    return React.Children.toArray(node.props.children).filter(
      isFormCheckJSXElement,
    );
  } else {
    return React.Children.toArray(node).filter(isFormCheckJSXElement);
  }
}

/** 从 JSX 节点中抽取 <FormItem component="hidden" /> */
function extractHiddenJSXElements(node: any): any[] {
  const isHiddenItemJSXElement = (item: any) =>
    item.props.component === 'hidden';

  if (node == null) {
    return [];
  }
  // todo 可能要考虑嵌套的情况
  if (node.type == React.Fragment) {
    return React.Children.toArray(node.props.children).filter(
      isHiddenItemJSXElement,
    );
  } else {
    return React.Children.toArray(node).filter(isHiddenItemJSXElement);
  }
}

export function calculateArrayTableData({
  itemContent,
  itemCount,
  arrayModel,
}: FormArrayLayoutInput) {
  const dataSource: any[] = range(itemCount).map((itemIndex) => {
    const renderedContent = itemContent(
      itemIndex,
      arrayModel.getSubModel(itemIndex),
    );
    const formItems = extractFormItemJSXElements(renderedContent).result;
    const flattenFormItems = collectNodes(formItems, 'leaf-only');
    const effects = extractFormEffectJSXElements(renderedContent);
    const checks = extractFormCheckJSXElements(renderedContent);
    const hiddenItems = extractHiddenJSXElements(renderedContent);

    return {
      rowKey: arrayHelpers.getKey(arrayModel, itemIndex),
      itemIndex,
      formItems,
      flattenFormItems,
      effects,
      checks,
      hiddenItems,
    };
  });

  let columnsShape: any[];
  if (dataSource.length > 0) {
    // 表格列的数量以表格第一行为准，不同的 rowIndex 应该确保渲染同样数量的 Form.Item
    columnsShape = dataSource[0].formItems;
  } else {
    // dataSource 当前为空，但为了渲染表格列，我们仍需要用 rowIndex=0 调用一次 itemContent
    columnsShape = extractFormItemJSXElements(
      itemContent(0, arrayModel.getSubModel(0)),
    ).result;
  }

  function convertColumn(item: any) {
    if (item.props.component === 'hidden') {
      return null;
    }
    return {
      render(_: any, row: any) {
        const formItem = row.flattenFormItems[item.colIndex];
        return React.cloneElement(formItem, {
          className: cx(formItem.className, 'minimal'),
        });
      },
      name: typeof item.props.label === 'string' ? item.props.label : null,
      title: (
        <>
          {(item.props.asterisk ?? item.props.required) && (
            <span className="required-indicator" />
          )}
          {item.props.label}
        </>
      ),
      ...(item.props['x-column'] ?? item.props['x-table-column']),
    };
  }

  function convertGroup(group: any): any {
    return {
      ...group.props,
      ...(group.props['x-column'] ?? group.props['x-table-column']),
      children: convert(group.children),
    };
  }

  const convert = (nodes: any[]): any[] => {
    return nodes
      .map((n) => (isLeafNode(n) ? convertColumn(n) : convertGroup(n)))
      .filter(Boolean);
  };

  const columns = convert(columnsShape);
  return { dataSource, columns };
}
